﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Scanner_Project
{
	public enum TokenType
	{
		Start,EndFile, Error,
		If, Then, Else, End, Repeat, Until, Read, Write, //Reserved Words
		ID, Number, //multicharacher tokens
		Assign, Equal, LessThan, GreatThan, Plus, Minus, Times, LeftParentheses, RightParentheses, SemiColon //special symbols
	};
	public enum StatusType
	{
		Start, InLetter, InDigit,InAssign, // Non Final Statu
		ID, Number,
		AddOperation, SubOperation, MulOperation,
		LeftBracket, RightBracket, LessThan, GreatThan, Equal, AssignOperator, Semicolon,
		InError, Error, EndFile
	};
	class Scanner
	{
		private int Index;
		private string Input;
		private string[] ReservedWords = { "If", "Then", "Else", "End", "Repeat", "Until", "Read", "Write" };
		public Scanner(string Text)
		{
			Input = Text;
			Index = 0;
		}
		void AdvanceIndex()
		{
			Index++;
		}
		char GetCurrentChar()
		{
			char ch='\0';
			if(Index<Input.Length)
				ch=Input[Index];
			return ch;
		}
		public Token GetToken()
		{
			Token CurrentToken = new Token();
			CurrentToken.Lexeme = "";
			StatusType CurrentStatu = StatusType.Start;
			char CurrentChar = GetCurrentChar();
			if (CurrentChar == '\0')
			{
				CurrentToken.Type = TokenType.EndFile;
				CurrentStatu = StatusType.EndFile;
			}
			while(!IsFinialStatu(CurrentStatu))
			{
				switch (CurrentStatu)
				{
					#region Case Start Statu
					case StatusType.Start:
						if (IsWhiteSpace(CurrentChar))
						{
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if (char.IsDigit(CurrentChar))
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InDigit;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if(char.IsLetter(CurrentChar))
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InLetter;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if(CurrentChar=='_')
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InLetter;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if (CurrentChar == '+')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.AddOperation;
							CurrentToken.Type = TokenType.Plus;
							AdvanceIndex();
						}
						else if (CurrentChar == '-')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.SubOperation;
							CurrentToken.Type = TokenType.Minus;
							AdvanceIndex();
						}
						else if (CurrentChar == '*')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.MulOperation;
							CurrentToken.Type = TokenType.Times;
							AdvanceIndex();
						}
						else if (CurrentChar == '(')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.LeftBracket;
							CurrentToken.Type = TokenType.LeftParentheses;
							AdvanceIndex();
						}
						else if (CurrentChar == ')')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.RightBracket;
							CurrentToken.Type = TokenType.RightParentheses;
							AdvanceIndex();
						}
						else if (CurrentChar == '<')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.LessThan;
							CurrentToken.Type = TokenType.LessThan;
							AdvanceIndex();
							//CurrentChar = GetCurrentChar();
						}
						else if (CurrentChar == '>')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.GreatThan;
							CurrentToken.Type = TokenType.GreatThan;
							AdvanceIndex();
						}
						else if (CurrentChar == ';')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.Semicolon;
							CurrentToken.Type = TokenType.SemiColon;
							AdvanceIndex();
						}
						else if(CurrentChar=='=')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.Equal;
							CurrentToken.Type = TokenType.Equal;
							AdvanceIndex();
						}
						else if(CurrentChar==':')
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.InAssign;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if (CurrentChar == '\0')
						{
							CurrentStatu = StatusType.EndFile;
							CurrentToken.Type = TokenType.EndFile;
						}
						else
						{
							CurrentToken.Lexeme = CurrentChar.ToString();
							CurrentStatu = StatusType.InError;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						break; 
					#endregion
					#region Case InDigit
					case StatusType.InDigit:
						if (char.IsDigit(CurrentChar))
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InDigit;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if (IsSpecialChar(CurrentChar)||CurrentChar=='\0')
						{
							CurrentStatu = StatusType.Number;
							CurrentToken.Type = TokenType.Number;
						}
						else if (IsWhiteSpace(CurrentChar))
						{
							CurrentStatu = StatusType.Number;
							CurrentToken.Type = TokenType.Number;
						}
						else if (char.IsLetter(CurrentChar))
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InError;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else
						{
							CurrentStatu = StatusType.Number;
							CurrentToken.Type = TokenType.Number;
						}
						break;
					#endregion
					#region Case InLetter
					case StatusType.InLetter:
						if (char.IsLetterOrDigit(CurrentChar) || CurrentChar == '_')
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InLetter;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else if (IsSpecialChar(CurrentChar) || CurrentChar == '\0')
						{
							CurrentStatu = StatusType.ID;
							CurrentToken.Type = TokenType.ID;
						}
						else if (IsWhiteSpace(CurrentChar))
						{
							CurrentStatu = StatusType.ID;
							CurrentToken.Type = TokenType.ID;
						}
						else
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InError;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						break; 
					#endregion
					#region Case InAssign
					case StatusType.InAssign:
						if (CurrentChar == '=')
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.AssignOperator;
							CurrentToken.Type = TokenType.Assign;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						else
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InError;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						break; 
					#endregion
					#region Case InError
					case StatusType.InError:
						if (IsSpecialChar(CurrentChar))
						{
							CurrentStatu = StatusType.Error;
							CurrentToken.Type = TokenType.Error;
						}
						else if (IsWhiteSpace(CurrentChar) || CurrentChar == '\0')
						{
							CurrentStatu = StatusType.Error;
							CurrentToken.Type = TokenType.Error;
						}
						else
						{
							CurrentToken.Lexeme += CurrentChar;
							CurrentStatu = StatusType.InError;
							AdvanceIndex();
							CurrentChar = GetCurrentChar();
						}
						break; 
					#endregion              
				}
			}
			#region Check if Reserved Words
			if (CurrentToken.Type == TokenType.ID)
			{
				if (CurrentToken.Lexeme.ToLower() == "if")
				{ CurrentToken.Type = TokenType.If; }
				else if (CurrentToken.Lexeme.ToLower() == "then")
				{ CurrentToken.Type = TokenType.Then; }
				else if (CurrentToken.Lexeme.ToLower() == "else")
				{ CurrentToken.Type = TokenType.Else; }
				else if (CurrentToken.Lexeme.ToLower() == "end")
				{ CurrentToken.Type = TokenType.End; }
				else if (CurrentToken.Lexeme.ToLower() == "repeat")
				{ CurrentToken.Type = TokenType.Repeat; }
				else if (CurrentToken.Lexeme.ToLower() == "until")
				{ CurrentToken.Type = TokenType.Until; }
				else if (CurrentToken.Lexeme.ToLower() == "read")
				{ CurrentToken.Type = TokenType.Read; }
				else if (CurrentToken.Lexeme.ToLower() == "write")
				{ CurrentToken.Type = TokenType.Write; }
			} 
			#endregion
			return CurrentToken;
		}
		bool IsFinialStatu(StatusType Statu)
		{
			switch (Statu)
			{
				case StatusType.Start:
				case StatusType.InDigit:
				case StatusType.InLetter:
				case StatusType.InError:
				case StatusType.InAssign:
					return false;
				default :
					return true;
			}
		}
		bool IsWhiteSpace(char character)
		{
			if (character == '\n' || character == '\r' || character == '\t' || character == ' ')
				return true;
			return false;
		}
		bool IsSpecialChar(char character)
		{
			switch (character)
			{
				case '+':
				case '-':
				case '*':
				case '(':
				case ')':
				case '<':
				case '>':
				case '=':
				case ';':
				case ':':
					return true;
				default :
					return false;
			}
		}
	}
}
